﻿#include "mserialportlist.h"
#include <QDebug>

MSerialPortList::MSerialPortList(qint16 cycle_ms, QObject *parent) : QThread(parent)
{
    qRegisterMetaType<QSerialPortInfoList>("QSerialPortInfoList");
    qRegisterMetaType<QSerialPortInfoList>("QSerialPortInfoList&");

    scanning_cycle_ms = cycle_ms;
    scanning_start_flag = false;
    scanning_timer.stop();
    serial_ports_info_list.clear();
    select = QSerialPortInfo(QString(""));

    moveToThread(this);
}

MSerialPortList::~MSerialPortList()
{
    scanning_stop();
    serial_ports_info_list.clear();
}

/**
 * @brief MSerialPortList::instance 获取串口扫描对象并开启串口扫描
 * @return 串口扫描对象指针
 */
MSerialPortList *MSerialPortList::instance(void)
{
    static QMutex mutex_instance;
    QMutexLocker locker(&mutex_instance);
    static MSerialPortList m_instance(SERIAL_SCANNING_CYCLE_MS);
    m_instance.scanning_start();
    return &m_instance;
}

/**
 * @brief 开启串口扫描，如果扫描到串口发生变化，
 * 则会通过void new_serial_port_update(const QSerialPortInfoList &)信号上报。
 * 执行获取对象函数instance()，会自动开启扫描
 * 可以通过scanning_stop()函数停止串口扫描
 * @return 0
 */
int MSerialPortList::scanning_start(void)
{
    QMutexLocker locker(&mutex_scanning);

    if(scanning_start_flag == false) {
        scanning_start_flag = true;
        scanning_timer.stop();
        scanning_timer.start(scanning_cycle_ms);
        QThread::start();
    }
    return 0;
}

/**
 * @brief 停止串口扫描，摧毁对象时会自动停止扫描
 * @return 0
 */
int MSerialPortList::scanning_stop(void)
{
    QMutexLocker locker(&mutex_scanning);

    if(scanning_start_flag == true) {
        scanning_start_flag = false;
        scanning_timer.stop();
        QThread::quit();
        QThread::wait();
    }
    return 0;
}

/**
 * @brief 获取被选中的串口
 * @return 选中的串口的信息指针
 */
const QSerialPortInfo &MSerialPortList::get_select_serial(void)
{
    QMutexLocker locker(&mutex_update);
    return select;
}

int MSerialPortList::set_select_serial(const QSerialPortInfo &serial)
{
    return set_select_serial(serial.portName());
}

int MSerialPortList::set_select_serial(const QString &port_name)
{
    QMutexLocker locker(&mutex_update);
    int index = -1;

    TAG_DEBUG("set_select_serial port_name = %s", port_name.toUtf8().data());

    for (int i = 0; i < serial_ports_info_list.size(); i++) {
        if (serial_ports_info_list.at(i).portName() == port_name) {
            index = i;
            break;
        }
    }
    if (index != -1) {
        select = serial_ports_info_list.at(index);
    } else {
        select = QSerialPortInfo(QString(""));
    }
    TAG_INFO("set selected serial name = %s", select.portName().toUtf8().data());
    return (index == -1);
}


void MSerialPortList::run(void)
{
    TAG_INFO("serial scan start.");
    qInfo() << "serial scan start.";
    connect(&scanning_timer, SIGNAL(timeout()),
            this, SLOT(on_scanning_timer_timeout()));
    exec();
    disconnect(&scanning_timer, SIGNAL(timeout()),
            this, SLOT(on_scanning_timer_timeout()));
    TAG_INFO("serial scan stop.");
}

void MSerialPortList::on_scanning_timer_timeout(void)
{
    QSerialPortInfoList new_list = QSerialPortInfo::availablePorts();
    bool is_serial_port_chance = false;
    bool is_serial_port_select = true;

    mutex_update.lock();
    if (new_list.size() != serial_ports_info_list.size()) {
        is_serial_port_chance = true;
    } else {
        for (int i = 0; i < serial_ports_info_list.size(); i++) {
            if (serial_ports_info_list.at(i).portName() != new_list.at(i).portName() ||
                serial_ports_info_list.at(i).description() != new_list.at(i).description() ||
                serial_ports_info_list.at(i).manufacturer() != new_list.at(i).manufacturer()) {
                is_serial_port_chance = true;
                break;
            }
        }
    }

    foreach(QSerialPortInfo info, new_list) {
        if (info.portName() == select.portName()) {
            is_serial_port_select = false;
            break;
        }
    }
    if (is_serial_port_select == true) {
        if (new_list.size() == 0) {
            select = QSerialPortInfo(QString(""));
        } else {
            select = new_list.at(0);
        }
    }

    serial_ports_info_list.clear();
    serial_ports_info_list.append(new_list);
    mutex_update.unlock();

    if (is_serial_port_chance == true) {
        emit new_serial_port_update(serial_ports_info_list);
    }
}
